import observer from './observe/index.js'
import { nextTick } from './utils/nextTick.js';
import Watcher from './observe/watcher'
import Dep from './observe/dep.js'

export default function initState(vm) {
  let opts = vm.$options;

  if (opts.data)
    initData(vm);
  if (opts.watch)
    initWatch(vm);
  if (opts.computed)
    initComputed(vm);
}

/**
 * data有两种写法
 * 1、 data(){return {}} 函数类型
 * 2、data{} 对象类型
 */
function initData(vm) {
  let data = vm.$options.data;
  // 如果是函数则执行，否则直接获取data。
  data = vm._data = typeof data === 'function' ? data.call(vm) : data;
  // data对象代理到实例上
  for (let key in data) {
    proxy(vm, '_data', key)
  }
  // data数据劫持
  observer(data);
}

/**
 * watch有多种写法
 * 1、函数 2、对象 3、数组 4、字符串
 */
function initWatch(vm) {
  let watch = vm.$options.watch;

  for (let key in watch) {
    let handler = watch[key];
    // 数组
    if (Array.isArray(handler)) {
      handler.forEach(item => {
        createWatcher(vm, key, item);
      })
    } else { // 对象、字符串
      createWatcher(vm, key, handler);
    }
  }
}

function createWatcher(vm, exprOrfn, handler, options) {
  if (typeof handler === 'object') {
    options = handler;
    handler = handler.handler;
  }
  if (typeof handler === 'string') {
    handler = vm[handler];
  }
  0 >= 0
  return vm.$watch(vm, exprOrfn, handler, options);
}

/**
 * computed有两种写法：
 * 1、函数 firstName(){}
 * 2、对象 firstName:{get(){},set(){}}
 */
function initComputed(vm) {
  let computed = vm.$options.computed;
  let watcher = vm._computedWatchers = {};

  for (let key in computed) {
    let userDef = computed[key];
    let getter = typeof userDef === 'function' ? userDef : userDef.get;
    // 每一个计算属性都要添加一个watcher
    watcher[key] = new Watcher(vm, getter, () => { }, { lazy: true })
    defineComputed(vm, key, userDef);
  }
}

let sharePropDefinition = {
  enumerable: true,
  configurable: true,
  get: () => { },
  set: () => { }
};
function defineComputed(target, key, userDef) {
  if (typeof userDef === 'function') {
    sharePropDefinition.get = createComputedGetter(key);
  } else {
    sharePropDefinition.get = createComputedGetter(key);
    sharePropDefinition.set = userDef.set;
  }

  Object.defineProperty(target, key, sharePropDefinition);
}

function createComputedGetter(key) {
  return function () {
    let watcher = this._computedWatchers[key];
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate();
      }
      if (Dep.target) {
        watcher.depend();
      }
      return watcher.value;
    }
  }
}

/**
 * 代理到实例上
 * @param vm
 * @param source
 * @param key
 */
function proxy(vm, source, key) {
  Object.defineProperty(vm, key, {
    get() {
      return vm[source][key]
    },
    set(newValue) {
      vm[source][key] = newValue;
    }
  })
}

export function stateMixin(vm) {
  vm.prototype.$nextTick = function (cb) {
    nextTick(cb);
  }
  vm.prototype.$watch = function (vm, exprOrfn, handler, options) {
    let watcher = new Watcher(vm, exprOrfn, handler, { ...options, user: true });
    // watch立即执行
    if (options.immediate) {
      handler.call(vm);
    }
  }
}